package com.insight.modules.system.controller;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import com.insight.common.api.vo.Result;
import com.insight.common.aspect.annotation.AutoLog;
import com.insight.common.system.query.QueryGenerator;
import com.insight.common.system.vo.DictModel;
import com.insight.common.util.oConvertUtils;
import com.insight.modules.system.entity.SysCategory;
import com.yuanqiao.insight.acore.system.vo.TreeSelectModel;
import com.insight.modules.system.service.ISysCategoryService;
import com.insight.modules.umpPwdManage.entity.UmpPwdManage;
import com.insight.modules.umpPwdManage.service.IUmpPwdManageService;
import com.insight.modules.util.ZipEncryUtils;
import org.jeecgframework.poi.excel.ExcelImportUtil;
import org.jeecgframework.poi.excel.entity.ImportParams;
import org.jeecgframework.poi.excel.view.JeecgEntityExcelView;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.MultipartHttpServletRequest;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
import java.util.*;
import java.util.stream.Collectors;

 /**
 * @Description: 分类字典
 * @Author: jeecg-boot
 * @Date:   2019-05-29
 * @Version: V1.0
 */
@RestController
@RequestMapping("/sys/category")
@Slf4j
@RestController
@RequestMapping("/sys/sysCategory")
@Slf4j
public class SysCategoryController {

	 // 分类字典业务层服务注入
	 @Autowired
	 private ISysCategoryService sysCategoryService;

	 // 密码管理服务注入，用于获取导出Excel的密码相关配置
	 @Autowired
	 private IUmpPwdManageService umpPwdManageService;

	 // ZIP文件加密工具类注入，用于加密导出
	 @Autowired
	 private ZipEncryUtils zipEncryUtils;

	 /**
	  * 根节点分页列表查询接口
	  *
	  * @param sysCategory 传入分类实体，主要用到pid作为查询条件
	  * @param pageNo      当前页码，默认1
	  * @param pageSize    每页数据条数，默认10
	  * @param req         HTTP请求对象
	  * @return 分页查询结果封装，包含分类字典列表
	  *
	  * 说明：
	  * - 当 pid 为空时，默认查询根节点（pid = "0"）
	  * - 查询时只根据pid做等值过滤，避免页面显示错误
	  */
	 @AutoLog(value = "分类字典-列表展示")
	 @GetMapping(value = "/rootList")
	 public Result<IPage<SysCategory>> queryPageList(SysCategory sysCategory,
													 @RequestParam(name = "pageNo", defaultValue = "1") Integer pageNo,
													 @RequestParam(name = "pageSize", defaultValue = "10") Integer pageSize,
													 HttpServletRequest req) {
		 // 判断传入pid是否为空，若为空则赋值为根节点"0"
		 if (oConvertUtils.isEmpty(sysCategory.getPid())) {
			 sysCategory.setPid("0");
		 }

		 Result<IPage<SysCategory>> result = new Result<>();

		 // 构建查询条件wrapper，只根据pid进行查询，保证父子关系正确显示
		 QueryWrapper<SysCategory> queryWrapper = new QueryWrapper<>();
		 queryWrapper.eq("pid", sysCategory.getPid());

		 // 创建分页对象，指定当前页和每页大小
		 Page<SysCategory> page = new Page<>(pageNo, pageSize);

		 // 调用Service执行分页查询
		 IPage<SysCategory> pageList = sysCategoryService.page(page, queryWrapper);

		 // 设置返回结果，标记成功
		 result.setSuccess(true);
		 result.setResult(pageList);

		 return result;
	 }

	 /**
	  * 查询所有符合条件的子分类（无分页）
	  *
	  * @param sysCategory 传入查询条件实体，可包含多个过滤字段
	  * @param req         HTTP请求对象，获取请求参数
	  * @return 返回符合条件的分类字典列表
	  */
	 @GetMapping(value = "/childList")
	 public Result<List<SysCategory>> queryPageList(SysCategory sysCategory, HttpServletRequest req) {
		 Result<List<SysCategory>> result = new Result<>();

		 // 通过QueryGenerator工具，根据请求参数和实体自动生成查询条件
		 QueryWrapper<SysCategory> queryWrapper = QueryGenerator.initQueryWrapper(sysCategory, req.getParameterMap());

		 // 查询符合条件的分类列表
		 List<SysCategory> list = sysCategoryService.list(queryWrapper);

		 // 设置返回结果
		 result.setSuccess(true);
		 result.setResult(list);
		 return result;
	 }

	 /**
	  * 新增分类字典
	  *
	  * @param sysCategory 前端传入的分类实体对象，JSON格式请求体
	  * @return 操作结果，成功或失败信息
	  */
	 @AutoLog(value = "分类字典-添加")
	 @PostMapping(value = "/add")
	 public Result<SysCategory> add(@RequestBody SysCategory sysCategory) {
		 Result<SysCategory> result = new Result<>();
		 try {
			 if (sysCategory == null){
				 return result.error500("参数不识别！");
			 }
			 // 调用Service层添加操作，含业务逻辑校验
			 sysCategoryService.addSysCategory(sysCategory);
			 result.success("添加成功！");
		 } catch (Exception e) {
			 // 记录异常日志
			 log.error(e.getMessage(), e);
			 // 返回操作失败信息
			 result.error500("操作失败");
		 }
		 return result;
	 }

	 /**
	  * 编辑分类字典
	  *
	  * @param sysCategory 传入的实体对象，必须包含ID字段
	  * @return 操作结果信息
	  *
	  * 逻辑：
	  * - 先查询数据库中是否存在该ID对应实体，防止编辑不存在的记录
	  * - 存在则更新，不存在返回错误
	  */
	 @AutoLog(value = "分类字典-编辑")
	 @PutMapping(value = "/edit")
	 public Result<SysCategory> edit(@RequestBody SysCategory sysCategory) {
		 Result<SysCategory> result = new Result<>();
		 // 根据ID查询实体，确认存在
		 SysCategory sysCategoryEntity = sysCategoryService.getById(sysCategory.getId());
		 if (sysCategoryEntity == null) {
			 result.error500("未找到对应实体");
		 } else {
			 // 存在则执行更新
			 sysCategoryService.updateSysCategory(sysCategory);
			 result.success("修改成功!");
		 }
		 return result;
	 }

	 /**
	  * 根据ID删除分类字典
	  *
	  * @param id 分类字典的ID，必填
	  * @return 删除操作结果
	  *
	  * 说明：
	  * - 先查询是否存在该实体，避免删除不存在的记录
	  * - 删除成功返回成功提示
	  */
	 @AutoLog(value = "分类字典-删除")
	 @DeleteMapping(value = "/delete")
	 public Result<SysCategory> delete(@RequestParam(name = "id", required = true) String id) {
		 Result<SysCategory> result = new Result<>();
		 SysCategory sysCategory = sysCategoryService.getById(id);
		 if (sysCategory == null) {
			 result.error500("未找到对应实体");
		 } else {
			 boolean ok = sysCategoryService.removeById(id);
			 if (ok) {
				 result.success("删除成功!");
			 }
		 }
		 return result;
	 }

	 /**
	  * 批量删除分类字典
	  *
	  * @param ids 多个ID字符串，用逗号分隔
	  * @return 操作结果
	  *
	  * 说明：
	  * - 参数不能为空
	  * - 将逗号分隔的ID转换为List后批量删除
	  */
	 @DeleteMapping(value = "/deleteBatch")
	 public Result<SysCategory> deleteBatch(@RequestParam(name = "ids", required = true) String ids) {
		 Result<SysCategory> result = new Result<>();
		 if (ids == null || "".equals(ids.trim())) {
			 result.error500("参数不识别！");
		 } else {
			 this.sysCategoryService.removeByIds(Arrays.asList(ids.split(",")));
			 result.success("删除成功!");
		 }
		 return result;
	 }

	 /**
	  * 根据ID查询单条分类字典信息
	  *
	  * @param id 分类字典ID，必填
	  * @return 查询到的分类实体或错误信息
	  */
	 @GetMapping(value = "/queryById")
	 public Result<SysCategory> queryById(@RequestParam(name = "id", required = true) String id) {
		 Result<SysCategory> result = new Result<>();
		 SysCategory sysCategory = sysCategoryService.getById(id);
		 if (sysCategory == null) {
			 result.error500("未找到对应实体");
		 } else {
			 result.setResult(sysCategory);
			 result.setSuccess(true);
		 }
		 return result;
	 }

	 /**
	  * 导出分类字典为Excel文件（支持密码加密）
	  *
	  * @param request     HTTP请求对象，获取请求参数
	  * @param sysCategory 查询条件实体
	  * @param response    HTTP响应对象，用于写出文件
	  *
	  * 说明：
	  * - 先根据查询条件获取列表数据
	  * - 再获取加密密码配置
	  * - 最后调用工具类生成加密Excel并写入响应
	  */
	 @RequestMapping(value = "/exportXls")
	 @AutoLog(value = "分类字典-导出")
	 public void exportXls(HttpServletRequest request, SysCategory sysCategory, HttpServletResponse response) {
		 // Step 1: 查询数据
		 QueryWrapper<SysCategory> queryWrapper = QueryGenerator.initQueryWrapper(sysCategory, request.getParameterMap());
		 List<SysCategory> pageList = sysCategoryService.list(queryWrapper);

		 // Step 2: 获取导出密码配置
		 UmpPwdManage zip = umpPwdManageService.getZip();

		 // Step 3: 调用工具类生成带密码的Excel文件，并写出响应
		 zipEncryUtils.zipPwd(response, SysCategory.class, "分类字典列表", zip.getZipPwd(), zip.getIsEncry(), pageList);
	 }

	 /**
	  * Excel导入分类字典数据接口
	  *
	  * @param request  上传文件请求
	  * @param response 响应对象
	  * @return 导入结果提示
	  *
	  * 说明：
	  * - 支持批量导入Excel文件
	  * - 自动根据编码推断父节点，确保树形结构正确
	  * - 导入过程中捕获异常并返回失败信息
	  */
	 @AutoLog(value = "分类字典-导入")
	 @RequestMapping(value = "/importExcel", method = RequestMethod.POST)
	 public Result<?> importExcel(HttpServletRequest request, HttpServletResponse response) {
		 if (request == null) {
			 return Result.error("请求对象为空");
		 }
		 MultipartHttpServletRequest multipartRequest = (MultipartHttpServletRequest) request;
		 Map<String, MultipartFile> fileMap = multipartRequest.getFileMap();
		 if (fileMap == null || fileMap.isEmpty()) {
			 return Result.error("未包含上传文件");
		 }
		 for (Map.Entry<String, MultipartFile> entity : fileMap.entrySet()) {
			 if (entity == null || entity.getValue() == null) {
				 continue;
			 }
			 MultipartFile file = entity.getValue();
			 ImportParams params = new ImportParams();
			 params.setTitleRows(2);
			 params.setHeadRows(1);
			 params.setNeedSave(true);
			 try (InputStream inputStream = file.getInputStream()) {
				 List<SysCategory> listSysCategorys = ExcelImportUtil.importExcel(inputStream, SysCategory.class, params);
				 if (listSysCategorys != null) {
					 Collections.sort(listSysCategorys);
					 log.info("排序后的list====>", listSysCategorys);
					 for (SysCategory sysCategoryExcel : listSysCategorys) {
						 if (sysCategoryExcel == null) {
							 continue;
						 }
						 String code = sysCategoryExcel.getCode();
						 if (code != null && code.length() > 3) {
							 String pCode = code.substring(0, code.length() - 3);
							 log.info("pCode====>", pCode);
							 String pId = sysCategoryService != null ? sysCategoryService.queryIdByCode(pCode) : null;
							 log.info("pId====>", pId);
							 if (StringUtils.isNotBlank(pId)) {
								 sysCategoryExcel.setPid(pId);
							 }
						 } else {
							 sysCategoryExcel.setPid("0");
						 }
						 if (sysCategoryService != null) {
							 sysCategoryService.save(sysCategoryExcel);
						 }
					 }
					 return Result.ok("文件导入成功！数据行数：" + listSysCategorys.size());
				 }
			 } catch (Exception e) {
				 log.error(e.getMessage(), e);
				 return Result.error("文件导入失败：" + e.getMessage());
			 }
		 }
		 return Result.error("文件导入失败！");
	 }

	 /**
	  * 根据指定字段和值加载单条分类数据（通常用于表单回显）
	  *
	  * @param field  查询字段名
	  * @param val    查询字段对应值
	  * @return       查询结果，单条记录或错误提示
	  */
	 @RequestMapping(value = "/loadOne", method = RequestMethod.GET)
	 public Result<SysCategory> loadOne(@RequestParam(name="field") String field,@RequestParam(name="val") String val) {
		 Result<SysCategory> result = new Result<SysCategory>();
		 try {
			 if (field == null || val == null) {
				 result.setMessage("查询字段或值为空");
				 result.setSuccess(false);
				 return result;
			 }
			 QueryWrapper<SysCategory> query = new QueryWrapper<SysCategory>();
			 query.eq(field, val);
			 List<SysCategory> ls = this.sysCategoryService != null ? this.sysCategoryService.list(query) : null;
			 if (ls == null || ls.size() == 0) {
				 result.setMessage("查询无果");
				 result.setSuccess(false);
			 } else if (ls.size() > 1) {
				 result.setMessage("查询数据异常,["+field+"]存在多个值:"+val);
				 result.setSuccess(false);
			 } else {
				 result.setSuccess(true);
				 result.setResult(ls.get(0));
			 }
		 } catch (Exception e) {
			 e.printStackTrace();
			 result.setMessage(e.getMessage());
			 result.setSuccess(false);
		 }
		 return result;
	 }

	 /**
	  * 根据父节点ID加载子节点数据，用于树控件异步加载
	  *
	  * @param pid 父节点ID
	  * @return 子节点列表封装成TreeSelectModel结构
	  */
	 @RequestMapping(value = "/loadTreeChildren", method = RequestMethod.GET)
	 public Result<List<TreeSelectModel>> loadTreeChildren(@RequestParam(name="pid") String pid) {
		 Result<List<TreeSelectModel>> result = new Result<List<TreeSelectModel>>();
		 try {
			 if (pid == null) {
				 result.setMessage("父ID不能为空");
				 result.setSuccess(false);
				 return result;
			 }
			 List<TreeSelectModel> ls = this.sysCategoryService != null ? this.sysCategoryService.queryListByPid(pid) : null;
			 result.setResult(ls);
			 result.setSuccess(true);
		 } catch (Exception e) {
			 e.printStackTrace();
			 result.setMessage(e.getMessage());
			 result.setSuccess(false);
		 }
		 return result;
	 }

	 /**
	  * 加载一级节点列表，支持同步（加载所有子节点）或异步加载
	  *
	  * @param async 是否异步加载子节点，true表示异步加载
	  * @param pcode 父编码，用于查询节点
	  * @return 一级节点列表，若同步加载则含所有子节点
	  */
	 @RequestMapping(value = "/loadTreeRoot", method = RequestMethod.GET)
	 public Result<List<TreeSelectModel>> loadTreeRoot(@RequestParam(name="async") Boolean async,@RequestParam(name="pcode") String pcode) {
		 Result<List<TreeSelectModel>> result = new Result<List<TreeSelectModel>>();
		 try {
			 if (pcode == null) {
				 result.setMessage("pcode不能为空");
				 result.setSuccess(false);
				 return result;
			 }
			 List<TreeSelectModel> ls = this.sysCategoryService != null ? this.sysCategoryService.queryListByCode(pcode) : null;
			 if (ls != null && !async) {
				 loadAllCategoryChildren(ls);
			 }
			 result.setResult(ls);
			 result.setSuccess(true);
		 } catch (Exception e) {
			 e.printStackTrace();
			 result.setMessage(e.getMessage());
			 result.setSuccess(false);
		 }
		 return result;
	 }

	 /**
	  * 递归加载所有子节点，用于同步加载树结构
	  *
	  * @param ls 父节点列表
	  */
	 private void loadAllCategoryChildren(List<TreeSelectModel> ls) {
		 for (TreeSelectModel tsm : ls) {
			 // 查询当前节点的直接子节点
			 List<TreeSelectModel> temp = this.sysCategoryService.queryListByPid(tsm.getKey());
			 if (temp != null && !temp.isEmpty()) {
				 // 设置当前节点的子节点集合
				 tsm.setChildren(temp);
				 // 递归继续加载子节点的子节点
				 loadAllCategoryChildren(temp);
			 }
		 }
	 }

	 /**
	  * 校验编码规范
	  *
	  * @param pid  父节点ID
	  * @param code 当前编码
	  * @return 编码是否符合规范，成功或失败提示
	  *
	  * 说明：
	  * - 传入的编码必须以父节点编码开头，否则返回错误
	  * - 如果pid为空，不做校验直接返回成功
	  */
	 @GetMapping(value = "/checkCode")
	 public Result<?> checkCode(@RequestParam(name = "pid", required = false) String pid, @RequestParam(name = "code", required = false) String code) {
		 if (oConvertUtils.isEmpty(code)) {
			 return Result.error("错误,类型编码为空!");
		 }
		 if (oConvertUtils.isEmpty(pid)) {
			 return Result.ok();
		 }
		 // 查询父节点实体
		 SysCategory parent = this.sysCategoryService.getById(pid);
		 if (code.startsWith(parent.getCode())) {
			 return Result.ok();
		 } else {
			 return Result.error("编码不符合规范,须以\"" + parent.getCode() +

